#include "MqttClient.hpp"

MqttClient::MqttClient(const std::string &client_id,
               const std::string &host,
               int port,
               const std::string &username,
               const std::string &password,
               int keepalive):client_id_(client_id),
                host_(host),
                port_(port),
                username_(username),
                password_(password),
                keepalive_(keepalive)
{
    mosquitto_lib_init(); // 初始化mqtt运行环境
    /**
     *  mosquitto_new 参数解析
     *  - id mqtt客户端的id
     *  - clean_session 指示是否清除上次连接时保留的会话状态
     *  - obj 一个可选的用户自定义的数据指针，会传入回调函数中的void* userdata参数
    */
    mosq_ = mosquitto_new(client_id.c_str(), true, this);
    if (!mosq_) {
        throw std::runtime_error("创建 MQTT 客户端失败");
    }

    if (!username_.empty()) {
        // 配置云端的用户名和密码，如果用户名是NULL的话则密码将会被忽略。
        mosquitto_username_pw_set(mosq_, username_.c_str(), password_.c_str());
    }

    /**
     * 这里两个函数是用来设置连接成功的时和接收到Broker的消息时的回调函数的。
     * 注意：因为这两个函数是标准的c函数，所以其不能传入类成员函数。我们在静态函数中调用
     * 成员函数，然后将静态函数作为回调函数传入。
     * 
    */
    mosquitto_connect_callback_set(mosq_,on_connect_static);
    mosquitto_message_callback_set(mosq_,on_message_static);
}

MqttClient::~MqttClient(){
    stop();
    if(mosq_){
        mosquitto_destroy(mosq_); //清理mqtt客户端
    }
    mosquitto_lib_cleanup(); // 清理mqtt运行环境
    std::cout << "[MqttClient] 已清理\n";
}

// mosquitto_loop_start函数是干啥的？
bool MqttClient::connect(){
    int rc = mosquitto_connect(mosq_,host_.c_str(),port_,keepalive_);
    if(rc != MOSQ_ERR_SUCCESS){
        std::cerr << "[MqttClient] 连接失败: " << mosquitto_strerror(rc) << '\n';
        return false; 
    }
    /**
     * mosquitto_loop_start 的作用
     * 1.创建一个独立的后台线程（mosquitto_loop_forever是堵塞的，mosquitto_loop是手动调用处理各种事务的）
     * 2.在线程中自动循环执行以下操作
     *  维护与服务器的网络连接；
     *  发送心跳包（PINGREQ）
     *  接收来自服务器的消息；
     *  处理 QoS 消息确认；
     *  触发你注册的各种回调函数：
     *      on_connect,on_message,on_disconnect等。
     */
    mosquitto_loop_start(mosq_);
    std::cout << "[MqttClient] 已连接并启动循环进程 \n";
    return true;
}

void MqttClient::stop(){
    if(mosq_){
        mosquitto_loop_stop(mosq_,true);
        mosquitto_disconnect(mosq_);
    }
}

bool MqttClient::publish(const std::string &topic, const std::string &payload, int qos, bool retain){
    /**
     *  mid （int*） 可选的消息ID指针，不关系可以传入nullptr
     *  payloadlen 消息内容长度
     *  payload 要发送的数据（二进制或字符串）
     *  qos 服务质量等级
     *  retain 是否让服务器保留这条消息，让后来的订阅者立即收到。
     */
    int rc = mosquitto_publish(mosq_, nullptr, topic.c_str(), payload.size(), payload.c_str(), qos, retain);
    if (rc != MOSQ_ERR_SUCCESS) {
        std::cerr << "[MqttClient] 发布失败: " << mosquitto_strerror(rc) << "\n";
        return false;
    }
    return true;
}

bool MqttClient::subscribe(const std::string &topic, int qos) {
    int rc = mosquitto_subscribe(mosq_, nullptr, topic.c_str(), qos);
    if (rc != MOSQ_ERR_SUCCESS) {
        std::cerr << "[MqttClient] 订阅失败: " << mosquitto_strerror(rc) << "\n";
        return false;
    }
    return true;
}


void MqttClient::onConnect(int rc){
    std::cout << "[MqttClient] 连接回调" << rc << '\n';
}

void MqttClient::onMessage(const std::string & topic,const std::string & payload){
    std::cout << "[MqttClient] 收到消息" << topic << " => " << payload << '\n';
}

/**
 * 这里的userdata就是在调用mosquitto_new时传入的this指针，只不过是void*类型
 * 通过static_cast<MqttClient*>转换为MqttClient*类型，也就是this指针了。
*/
void MqttClient::on_connect_static(struct mosquitto *mosq, void *userdata, int rc){
    auto * self = static_cast<MqttClient*>(userdata);
    if(self){
        self->onConnect(rc);
    }
}

void MqttClient::on_message_static(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *msg){
    auto* self = static_cast<MqttClient*>(userdata);
    if(self && msg->payload){
        self->onMessage(msg->topic,std::string((char*)msg->payload,msg->payloadlen));
    }
}